package com.tankz.systems.camera;

import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Input;
import org.newdawn.slick.MouseListener;

import com.artemis.Aspect;
import com.artemis.ComponentMapper;
import com.artemis.Entity;
import com.artemis.EntitySystem;
import com.artemis.managers.TagManager;
import com.artemis.utils.ImmutableBag;
import com.tankz.components.Physics;
import com.tankz.systems.misc.BoundarySystem;

public class CameraSystem extends EntitySystem implements MouseListener {
	private GameContainer container;

	private BoundarySystem boundarySystem;

	private Entity player;

	private ComponentMapper<Physics> physicsMapper;
	
	private float lookAtX;
	private float lookAtY;
	
	private float targetZoom;
	private float zoom;
	
	private int screenWidth;
	private int screenHeight;

	private Input input;

	public CameraSystem(GameContainer container) {
		super(Aspect.getAspectFor());
		this.container = container;
	}

	@Override
	public void initialize() {
		physicsMapper = world.getMapper(Physics.class);

		ensurePlayerEntity();
		
		boundarySystem = world.getSystem(BoundarySystem.class);
		
		input = container.getInput();
		input.addMouseListener(this);
		
		zoom = targetZoom = 1;
		
		screenWidth = container.getWidth();
		screenHeight = container.getHeight();
	}

	@Override
	protected void processEntities(ImmutableBag<Entity> entities) {
		ensurePlayerEntity();

		if (player != null) {
			updatePosition();
			updateZoom();
			fixBoundaries();
			input.setOffset(getStartX(), getStartY());
		}
	}

	private void updatePosition() {
		Physics physics = physicsMapper.get(player);
		lookAtX = physics.getX();
		lookAtY = physics.getY();
	}
	
	
	private void updateZoom() {
		if (targetZoom != zoom) {
			if (targetZoom > zoom) {
				zoom += 0.0005f * (float)world.getDelta();
				if (zoom > targetZoom) {
					zoom = targetZoom;
				}
			} else {
				zoom -= 0.0005f * (float)world.getDelta();
				if (zoom < targetZoom) {
					zoom = targetZoom;
				}
			}

			input.setScale(1f / zoom, 1f / zoom);
		}
	}
	
	private void fixBoundaries() {
		if (getEndX() > boundarySystem.getBoundaryWidth()) {
			lookAtX -= getEndX() - boundarySystem.getBoundaryWidth();
		} else if (getStartX() < 0) {
			lookAtX -= getStartX();
		}

		if (getEndY() > boundarySystem.getBoundaryHeight()) {
			lookAtY -= getEndY() - boundarySystem.getBoundaryHeight();
		} else if (getStartY() < 0) {
			lookAtY -= getStartY();
		}
	}
	
	@Override
	protected boolean checkProcessing() {
		return true;
	}
	
	private float getOffsetX() {
		return ((1f / zoom) * (float) screenWidth) / 2f;
	}

	private float getOffsetY() {
		return ((1f / zoom) * (float) screenHeight) / 2f;
	}

	public float getStartX() {
		return lookAtX - getOffsetX();
	}

	public float getStartY() {
		return lookAtY - getOffsetY();
	}

	public float getEndX() {
		return lookAtX + getOffsetX();
	}

	public float getEndY() {
		return lookAtY + getOffsetY();
	}

	public float getWidth() {
		return getEndX() - getStartX();
	}

	public float getHeight() {
		return getEndY() - getStartY();
	}
	
	public float getZoom() {
		return zoom;
	}

	private void ensurePlayerEntity() {
		if (player == null || !player.isActive())
			player = world.getManager(TagManager.class).getEntity("PLAYER");
	}

	@Override
	public void mouseClicked(int button, int x, int y, int clickCount) {
	}

	@Override
	public void mouseDragged(int oldx, int oldy, int newx, int newy) {
	}

	@Override
	public void mouseMoved(int oldx, int oldy, int newx, int newy) {
	}

	@Override
	public void mousePressed(int button, int x, int y) {
	}

	@Override
	public void mouseReleased(int button, int x, int y) {
	}

	@Override
	public void mouseWheelMoved(int change) {
		if (change > 0) {
			targetZoom += 0.25f;
			if (targetZoom > 1.5f) {
				targetZoom = 1.5f;
			}
		} else {
			targetZoom -= 0.25f;
			if (targetZoom < 0.5f) {
				targetZoom = 0.5f;
			}
		}
	}

	@Override
	public void inputEnded() {
	}

	@Override
	public void inputStarted() {
	}

	@Override
	public boolean isAcceptingInput() {
		return true;
	}

	@Override
	public void setInput(Input input) {
	}

}
